/**
 * v8class.h        Template based wrapper classs for implementing a V8 class that can be accessed
 *                  from JavaScript.
 *
 * Version:         1.0
 *
 * Copyright (c) Bal Bahadur Chettri
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are permitted provided 
 * that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this list of conditions and the 
 * following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list 
 * of conditions and the following disclaimer in the documentation and/or other materials provided with the 
 * distribution.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE 
 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Last Updated: Dec 10 2011
 */
#ifndef __V8HOST_V8CLASS_H
 #define __V8HOST_V8CLASS_H

#include <v8.h>

#include "v8class_instance_manager.h"

using namespace v8;

/** V8Class template class is the base class for all C++ classes that is exposed to v8. */
template <class _T>
class V8Class {
protected:
    /** ObjectTemplate for this class where we set all methods and proeprties JS can invoke on the 
     objects of this type */
	static Persistent<ObjectTemplate> _template;
    
    /** Weak reference to the v8 Object. */
	Persistent<Object> m_weak_ref;

	virtual ~V8Class () {
	}

protected:
    /**
     * Implements the constructor function for the v8 Objects.
     */
	static Handle<Value> _ctor (const Arguments &args) {
		HandleScope handle_scope;
		Local<Object> obj_point = NewInstance ();
		return handle_scope.Close (obj_point);
	}

    /**
     * This WeakReferenceCallback is called when all references to the v8 Object are released and only 
     * existing references are weak. This is a perfect place to make sure that no one is using the object
     * except this class through the weak reference, which we don't need any more.
     */
	static void _dtor (Persistent<Value> object, void *param) {
		HandleScope handle_scope;
		
        _T *instance = static_cast<_T*>(param);
        
        // destroy the waek reference
		instance->m_weak_ref.Dispose ();
		instance->m_weak_ref.Clear ();
        
        // free the native instance
        delete instance;

		// remove the object from the instance manager since it no longer exists
		V8ClassInstanceManager::RemoveInstance (instance);
	}

    /**
     * This is called when the instance manager wants to destroy the instance since v8 has already exited 
     * without garbage collecting the object and is never going to call the WeakReferenceCallback _dtor. 
     * We destroy the object and release the weak reference we no longer need.
     */
	static void _ForceDestroy (void *_instance) {
		_T *instance = static_cast<_T*>(_instance);

		instance->m_weak_ref.Dispose ();
		instance->m_weak_ref.Clear ();
		delete instance;

		// Note: It is not ncessary to remove the object from the instance manager since this method is called 
        // itself by the V8ClassInstanceManager class.
	}

public:
    /**
     * Returns a populated ObjectTemplate for this class, which can be used for instantiating the objects.
     * Additional properties and methods can be set in the ObjectTemplate if required.
     */
	static Local<ObjectTemplate> GetTemplate () {
		HandleScope handle_scope;
		
        // create the template only if required
		if (_template.IsEmpty()) {
			HandleScope handle_scope;

			// create a local object template
			Local<ObjectTemplate> __template = ObjectTemplate::New ();
            
            // create the actual persistent template
			_template = Persistent<ObjectTemplate>::New (__template);
			_template->SetInternalFieldCount (1);

			// set properties and methods...
			_T::_RegisterProperties ();
			_T::_RegisterMethods ();
		}
		
        // create and return a local ObjectTemplate from our persistent template
		Local<ObjectTemplate> ret = Local<ObjectTemplate>::New (_template);
		return handle_scope.Close(ret);
	}

    /**
     * Creates a new instance of v8 Object from the ObjectTemplate. This also creates
     * the native instance partner and wraps it within the v8 Object.
     */
	static Local<Object> NewInstance () {
		HandleScope handle_scope;
		
		// force template creation before we create the object
		GetTemplate ();

		// allocate a new instance of the native class
		V8Class *instance = new _T;

		// create a V8 object and wrap the native instance in it
		Local<Object> object = _template->NewInstance ();
		object->SetPointerInInternalField (0, instance);

		// Save a weak reference to the V8 object in the native instance
		instance->m_weak_ref = Persistent<Object>::New (object);
		instance->m_weak_ref.MakeWeak (instance, _T::_dtor);

		// register the instance with the V8ClassInstanceManager for
		// proper memory cleanup
		V8ClassInstanceManager::AddInstance (instance, _ForceDestroy);

        // Return the v8 object
		return handle_scope.Close (object);
	}
    
    /**
     * Performs cleanup. This must be called once before program termination to perform 
     * necessary cleanup of interal resources like static objects.
     */
    void Cleanup () {
        // Destroy the object template
        if (!_template.IsEmpty()) {
			_template.Dispose ();
			_template.Clear ();
		}
    }
};

#endif /* __V8HOST_V8CLASS_H */
